package easy.trace.core.util;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author easy
 */
public class RpcIdGenerator {

    private static final int MAX_SERVICE_LIMIT = 200;
    private static final int MAX_SERVICE_OCCURRENCES_LIMIT = 20;
    private final ConcurrentHashMap<String, String> serviceMap = new ConcurrentHashMap<>(MAX_SERVICE_LIMIT);

    private static class RpcIdGenHolder {
        private static final RpcIdGenerator instance = new RpcIdGenerator();
    }

    public static RpcIdGenerator get() {
        return RpcIdGenHolder.instance;
    }

    public String append(String interfaceId, String methodName) {
        return append(null, interfaceId, methodName);
    }

    /**
     * RpcId 由以下规则组成<br/>
     * 根据服务名称的md5值 连接而成
     * <p>
     * 注意：这里的位，是指“.”作为分隔符所占的位数，非字符串长度的位数。
     * RpcId 为不定长字符串，但保证在分布式集群条件下的唯一性
     *
     * @return
     */
    public String append(final String rpcId, String interfaceId, String methodName) {
        final String code = getMD5(interfaceId, methodName);
        if (StringUtils.isEmpty(rpcId)) {
            return code;
        }
        if (rpcId.contains(code)) {
            return rpcId;
        }
        if (StringUtils.countOccurrencesOfPoint(rpcId) == MAX_SERVICE_OCCURRENCES_LIMIT) {
            return rpcId;
        }
        if (StringUtils.nonEmpty(rpcId) && StringUtils.nonEmpty(code)) {
            return combine(rpcId, code);
        }

        return "0";
    }

    public static String combine(String oldData, String newData) {
        StringBuilder sb = new StringBuilder();
        sb.append(oldData);
        sb.append(".");
        sb.append(newData);
        return sb.toString();
    }

    private String getMD5(String interfaceId, String methodName) {
        String serviceInfo = getServiceInfo(interfaceId, methodName);
        if (StringUtils.isEmpty(serviceInfo)) {
            return "0";
        }
        if (serviceMap.containsKey(serviceInfo)) {
            return serviceMap.get(serviceInfo);
        } else {
            String md5 = calculateMD5(serviceInfo);
            if (serviceMap.size() < MAX_SERVICE_LIMIT) {
                serviceMap.put(serviceInfo, md5);
            }
            return md5;
        }
    }


    private static String getServiceInfo(String interfaceId, String methodName) {
        if (StringUtils.isEmpty(interfaceId)) {
            return null;
        }
        if (interfaceId.equalsIgnoreCase(methodName)) {
            return interfaceId;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(interfaceId);
        sb.append(":");
        sb.append(methodName);
        return sb.toString();
    }

    private static String calculateMD5(String serviceInfo) {
        if (StringUtils.isEmpty(serviceInfo)) {
            return null;
        }
        return MD5Utils.MD5Encrypt(serviceInfo);
    }

}
